/*

 * Copyright (c) 2017-2018, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package of.hotswapinject.injection;

import of.hotswapinject.core.common.Util;
import of.hotswapinject.core.plugin.*;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MethodHook implements IMethodHook
{
    private IMethodFilter methodFilter = null;
    private String className;
    private boolean isHooked;

    private static Map<String, MethodHook> hookManagerMap = new ConcurrentHashMap<String, MethodHook>();
    public static final String MethodHookClassName;
    static {
        MethodHookClassName = Type.getInternalName(MethodHook.class);
    }

    public static IMethodEnterPlugin.ReturnParam onEnter(MethodMeta adviceAdapter, String key, Object[] params) {
        IMethodFilter methodFilter = getMethodFilter(key);
        if(methodFilter == null) {
            return IMethodEnterPlugin.ReturnParam.none();
        }
        return methodFilter.onEnter(adviceAdapter, key, params);
    }

    public static IMethodExitPlugin.ReturnParam onExit(Object result, MethodMeta adviceAdapter, String key, int opCode, Object[] params) {
        IMethodFilter methodFilter = getMethodFilter(key);
        if(methodFilter == null) {
            return IMethodExitPlugin.ReturnParam.none();
        }
        return methodFilter.onExit(adviceAdapter, opCode, key, params, result);
    }

    public static IMethodFilter getMethodFilter(String key) {
        MethodHook methodHook = hookManagerMap.get(key);
        if(methodHook == null) {
            return null;
        }
        return methodHook.methodFilter;
    }

    public boolean computeFrames() {
        return true;
    }


    public MethodHook(IMethodFilter methodFilter, String className, boolean isHooked) {
        this.methodFilter = methodFilter;
        this.className = className;
        this.isHooked = isHooked;
    }


    public MethodVisitor hook(int access, String name, String desc, String signature, String[] exceptions, MethodVisitor mv) {
        try {
            /*
            //undo:test
            if("<init>".equals(name)) {
                return mv;
            }
            */

            final String key = String.join(Util.sep, new String[]{ className, name, desc });
            hookManagerMap.put(key, this);
            if(isHooked)
            {
                return mv;
            }
            return new MethodHookAdvice(key, Opcodes.ASM5, mv, access, name, desc);
        }
        catch (Throwable t) {
            t.printStackTrace();
            return mv;
        }
    }
}


